package com.xy.sql.xml.core;

import com.xy.sql.xml.fragment.IXySQLFragment;
import com.xy.sql.xml.fragment.XySQLFieldFragment;
import com.xy.sql.xml.fragment.XySQLStringFragment;
import com.xy.sql.xml.itf.XySQLRegister;
import com.xy.sql.xml.kit.XySQLDataUtil;
import com.xy.sql.xml.kit.XySQLScriptKit;
import com.xy.sql.xml.node.XySQLSuperNode;
import com.xy.sql.xml.node.impl.*;
import ognl.Ognl;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 标准钉钉
 */
public class XySQLKit {

    //开始文字
    private static String XML_BEGIN = "<mapper ";
    //开始文字
    private static String XML_END = "</mapper>";

    //支持得所有TAG
    private final static Map<String, XySQLSuperNode> tagMap = new HashMap<>();


    /**
     * 初始化到缓存中
     *
     * @param node
     */
    private static void put(XySQLSuperNode node) {
        tagMap.put(node.getTag(), node);
    }

    //支持得所有TAG 初始化
    static {
        put(new XySQLIfNode());
        put(new XySQLModelItemNode());
        put(new XySQLWhenNode());
        put(new XySQLOtherwiseNode());
        put(new XySQLForeachNode());

        put(new XySQLWhereNode());
        put(new XySQLMapperNode());
        put(new XySQLSelectNode());
        put(new XySQLInsertNode());
        put(new XySQLUpdateNode());
        put(new XySQLSetNode());
        put(new XySQLDeleteNode());
        put(new XySQLIncludeNode());
        put(new XySQLSqlNode());
        put(new XySQLChooseNode());
        put(new XySQLModelNode());

    }


    /**
     * 标准化SQL
     *
     * @return
     * @throws Exception
     */
    public static XySQLSuperNode parse(String sql) throws Exception {
        //分行
        List<String> lines = format(sql);
        LinkedList<XySQLSuperNode> stack = new LinkedList<>();
        XySQLStackVO lineVO;
        for (String line : lines) {
            if (line.length() <= 0) {
                continue;
            }
            lineVO = dispatch(line);
            if (lineVO == null) {
                continue;
            }
            //出栈
            if (lineVO.isEndFlag()) {
                List<XySQLSuperNode> childList = new ArrayList<>();
                XySQLSuperNode top;
                //出栈
                while (true) {
                    try {
                        top = stack.pop();
                        if (top.getClass().getName().equals(lineVO.getNodeClass().getName())) {
                            if (childList.size() > 0) {
                                Collections.reverse(childList);
                                top.setChildList(childList);
                            }
                            push(stack, top);
                            break;
                        } else {
                            childList.add(top);
                        }
                    } catch (Exception ex) {
                        throw new Exception(line + "没有找到开始符号");
                    }

                }
            } else {
                push(stack, lineVO.getNode());
            }
        }
        //取得根节点
        List<XySQLSuperNode> childList = new ArrayList<>();
        while (stack.size() > 1) {
            childList.add(stack.pop());
        }
        XySQLSuperNode top = stack.pop();
        if (childList.size() > 0) {
            Collections.reverse(childList);
            top.setChildList(childList);
        }
        //解析所有属性
        parseTxt(top);
        return top;
    }

    /**
     * 解析具体属性
     *
     * @return
     */
    private static void parseTxt(XySQLSuperNode node) throws Exception {
        node.parse();
        if (node.getChildList() != null) {
            for (XySQLSuperNode subNode : node.getChildList()) {
                parseTxt(subNode);
            }
        }
    }

    /**
     * 分发成具体的Tag
     *
     * @return
     */
    private static void push(LinkedList<XySQLSuperNode> stack, XySQLSuperNode node) throws Exception {
        if (node instanceof XySQLStringNode && stack.size() > 0) {
            XySQLSuperNode top = stack.peek();
            //合并到上个节点去
            if (top instanceof XySQLStringNode) {
                String newTxt = top.getTxt() + node.getTxt();
                top.setTxt(newTxt);
            }
            //直接添加
            else {
                stack.push(node);
            }
        } else {
            stack.push(node);
        }

    }

    /**
     * 分发成具体的Tag
     *
     * @param line
     * @return
     */
    private static XySQLStackVO dispatch(String line) throws Exception {
        Long start = System.currentTimeMillis();
        try {
            //空行不需要解析
            if (line == null || line.length() == 0) {
                return null;
            }
            XySQLSuperNode node;
            XySQLSuperNode temp;
            //必须有左右尖括号，才需要分析
            if (line.charAt(0) == XySQLScriptKit.START_SIGN && line.charAt(line.length() - 1) == XySQLScriptKit.END_SIGN) {
                StringBuilder tagName = new StringBuilder();
                char c;
                boolean endFlag = false;
                for (int i = 1; i < line.length(); i++) {
                    c = line.charAt(i);
                    //<if a=""
                    //<if>
                    //<if/>
                    if (c == ' ' || c == XySQLScriptKit.END_SIGN) {
                        break;
                    }
                    if (c == '/') {
                        if (i == 1) {
                            endFlag = true;
                            continue;
                        }
                        break;
                    }
                    tagName.append(c);
                }
                if (tagName.length() > 0) {
                    temp = tagMap.get(tagName.toString());
                    if (temp != null) {
                        if (endFlag) {
                            return new XySQLStackVO(temp.getClass(), true);
                        }
                        node = temp.clone();
                        node.setTxt(trimTagTxt(line));
                        return new XySQLStackVO(node, false);
                    }
                }
            }
            node = new XySQLStringNode();
            node.setTxt(line);
            return new XySQLStackVO(node, false);
        } finally {
            XySQLRegister.getLogger().debug("dispatch" + line + ",耗时：" + (System.currentTimeMillis() - start));
        }
    }


    /**
     * 删除左右特殊符号
     *
     * @param line
     * @return
     */
    private static String trimTagTxt(String line) {
        int len = line.length();
        if (line.charAt(len - 2) == '/') {
            return line.substring(1, len - 2);
        }
        return line.substring(1, len - 1);
    }

    /**
     * 标准化SQL，分成行
     *
     * @param script
     * @return
     * @throws Exception
     */
    public static List<String> format(String script) throws Exception {
        if (script == null || script.length() <= 0) {
            return new ArrayList<>();
        }
        //截取有效内容
        int begin = script.indexOf(XML_BEGIN);
        int end = script.indexOf(XML_END);
        if (begin < 0 || end < 0 || end - XML_BEGIN.length() <= begin) {
            return new ArrayList<>();
        }
        String txt = script.substring(begin, end + XML_END.length());
        if (txt.length() == 3) {
            return new ArrayList<>();
        }
        //分行
        return XySQLScriptKit.line(txt);
    }

    //表达式正则
    public static Pattern fragmentPattern = Pattern.compile("((\\$\\{[\\[\\]\\s\\w,=\"\'-\\.]*\\})|(\\#\\{[\\[\\]\\s\\w,=\"\'-\\.]*\\}))");

    /**
     * 解析SQL表达式
     *
     * @param sql
     * @return
     * @description 分析片段
     */
    public static List<IXySQLFragment> processFragment(String sql) throws Exception {
        String newSql = sql;
        Matcher m = fragmentPattern.matcher(newSql);
        List<IXySQLFragment> voList = new ArrayList<IXySQLFragment>();
        String preStr;
        String fieldStr;
        int begin;
        int end;
        char type;
        while (m.find()) {
            begin = m.start();
            if (begin > 0) {
                preStr = newSql.substring(0, begin);
                if (preStr != null && preStr.trim().length() > 0) {
                    voList.add(new XySQLStringFragment(preStr.trim()));
                }
            }
            fieldStr = m.group().trim();
            type = fieldStr.charAt(0);
            voList.add(new XySQLFieldFragment(m.group(), '$' == type));
            end = m.end();
            if (end < newSql.length()) {
                newSql = newSql.substring(end);
                m = fragmentPattern.matcher(newSql);
                continue;
            } else {
                newSql = "";
            }
            break;
        }
        if (newSql.trim().length() > 0) {
            voList.add(new XySQLStringFragment(newSql));
        }
        return voList;
    }
    // 下面是执行相关的了***********************************************************************************************

    /**
     * 执行表达式
     *
     * @param condition
     * @return
     * @description 预处理
     */
    public static boolean runPredicate(String condition, XySQLTaskContext task) {
        try {
            Object value = Ognl.getValue(condition, task.getConditionContext(), task.getConditionContext());
            if (value == null) {
                return false;
            }
            if (value instanceof Boolean) {
                return (Boolean) value;
            }
            String t = value.toString();
            if ("true".equalsIgnoreCase(t)) {
                return true;
            }
            return false;
        } catch (Exception ex) {
            XySQLRegister.getLogger().error("执行公式 : " + condition, ex);
            return false;
        }

    }

    /**
     * 取得属性值
     *
     * @return
     * @description 预处理
     */
    public static Object getValue(String fieldName, String type, XySQLTaskContext task) {
        try {
            Object value = Ognl.getValue("#" + fieldName, task.getConditionContext(), task.getConditionContext());
            if (value == null) {
                return null;
            }
            return XySQLDataUtil.convertValue(type, value);
        } catch (Exception ex) {
            XySQLRegister.getLogger().error("取得" + fieldName + "值异常", ex);
            return false;
        }

    }


    /**
     * 转换为SQL语句，绑定参数
     *
     * @param sqlId
     * @param inParamList
     * @param mapper
     * @return
     * @throws Exception
     */
    public static XySQLResult bound(String sqlId, Map<String, Object> inParamList, XySQLMapperNode mapper) throws Exception {
        long start = System.currentTimeMillis();
        //生成上下文
        XySQLTaskContext task = new XySQLTaskContext(mapper);
        //取得指定节点
        XySQLSuperNode node = task.getSqlFragmentById(sqlId);
        if (node == null) {
            return null;
        }
        List<XySQLSuperNode> nodeList = node.getChildList();
        if (nodeList == null || nodeList.size() == 0) {
            return null;
        }
        //参数转变,类型转换
        Map<String, Object> param = convert(inParamList, task.getItemMap());
        //初始化公式
        task.initPredicate(param);


        //继续绑定，生成参数
        List<XySQLExePara> list = new ArrayList<>();
        List<XySQLExePara> subList;
        for (XySQLSuperNode temp : nodeList) {
            subList = temp.boundSql(task);
            if (subList != null && subList.size() > 0) {
                list.addAll(subList);
            }
        }
        //构造返回结果
        StringBuilder sql = new StringBuilder();
        String fragment;
        List<Object> sqlParamList = new ArrayList<Object>();
        for (XySQLExePara fvo : list) {
            if (fvo == null) {
                continue;
            }
            fragment = fvo.getSqlFragment();
            sql.append(fragment).append(" ");
            if (fvo.isContainNull()) {
                sqlParamList.add(fvo.getValue());
            } else {
                if (fvo.getValue() != null) {
                    sqlParamList.add(fvo.getValue());
                }
            }
        }
        XySQLRegister.getLogger().debug("整个耗时：" + (System.currentTimeMillis() - start));
        return new XySQLResult(sql.toString(), sqlParamList);
    }

    /**
     * 根据模型转换参数
     *
     * @param itemMap
     * @return
     * @throws Exception
     */
    private static Map<String, Object> convert(Map<String, Object> inParamList, Map<String, Map<String, String>> itemMap) throws Exception {
        Map<String, Object> resMap = new HashMap<>();
        if (itemMap == null) {
            itemMap = new HashMap<>();
        }
        Map<String, String> nowProp;
        String type;
        for (Map.Entry<String, Object> entry : inParamList.entrySet()) {
            Object value = entry.getValue();
            resMap.put(entry.getKey(), value);
            if (itemMap.containsKey(entry.getKey()) && entry.getValue() != null) {
                nowProp = itemMap.get(entry.getKey());
                type = nowProp.get(XySQLModelItemNode.ATT_TYPE);
                //类型转换
                resMap.put(entry.getKey(), XySQLDataUtil.convertValue(type, value));
            }
        }
        //重新赋值
        inParamList.putAll(resMap);
        return inParamList;
    }


    /**
     * 根据模型转换参数
     *
     * @param context
     * @return
     * @throws Exception
     */
    public static Object convertOne(XySQLTaskContext context, String key, Object value) throws Exception {
        if (value == null) {
            return null;
        }
        Map<String, Map<String, String>> itemMap = context.getItemMap();
        if (itemMap == null) {
            itemMap = new HashMap<>();
        }
        if (itemMap.containsKey(key)) {
            Map<String, String> nowProp = itemMap.get(key);
            String type = nowProp.get(XySQLModelItemNode.ATT_TYPE);
            //类型转换
            return XySQLDataUtil.convertValue(type, value);
        }
        return value;
    }


}
